#pragma once

#include "Nodes/Defs.h"
#include <string>

template<class Node>
class PrettyPrinter : protected NodeVisitor<Node>
{
    std::string m_result;

private:
    void printChildWithParenthesis(NodeType curNodeType, Node * childNode)
    {
    	// Check if we need to add parenthesis
        bool addParens = (childNode->getNodeType() < curNodeType);

        if(addParens)
        	m_result += "(";

        // Process node
        childNode->accept(this);

        if(addParens)
        	m_result += ")";
    }

public:
    static inline std::string print(Node * node)
    {
    	PrettyPrinter printer;
    	node->accept(&printer);
    	return printer.m_result;
    }

    inline void visitOr(typename NodeTraits<Node>::NodeOr * node)
    {
        NodeType eCurNodeType = node->getNodeType();

        printChildWithParenthesis(eCurNodeType, node->getChild(0));
        for(size_t i=1; i<node->getChildrenCount(); ++i)
        {
        	m_result += " | ";
        	printChildWithParenthesis(eCurNodeType, node->getChild(i));
        }
    }

    void visitAnd(typename NodeTraits<Node>::NodeAnd * node)
    {
        NodeType eCurNodeType = node->getNodeType();

        printChildWithParenthesis(eCurNodeType, node->getChild(0));
        for(size_t i=1; i<node->getChildrenCount(); ++i)
        {
        	m_result += " & ";
        	printChildWithParenthesis(eCurNodeType, node->getChild(i));
        }
    }

    void visitNot(typename NodeTraits<Node>::NodeNot * node)
    {
    	m_result += "~";
    	printChildWithParenthesis(node->getNodeType(), node->getChild(0));
    }

    void visitAtom(typename NodeTraits<Node>::NodeAtom * node)
    {
        m_result += node->getAtomText();
    }
};

